package com.jzoom.zoom.admin.models;

import com.jzoom.zoom.common.Destroyable;
import com.jzoom.zoom.common.utils.Classes;
import com.jzoom.zoom.common.utils.ProcessUtils;
import com.jzoom.zoom.timer.annotation.Timer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class ShellService implements Destroyable {

    @Override
    public void destroy() {

        Classes.destroy(data);
    }


    class ShellSessionImpl implements ShellSession,ProcessUtils.ProcessListener {

        private ProcessUtils.ProcessController processController;

        private List<String> pool = new LinkedList<String>();

        private String sessionId;

        public ShellSessionImpl() throws IOException {
            processController = ProcessUtils.exec("sh",this);
            this.sessionId = UUID.randomUUID().toString();
            lastTime = System.currentTimeMillis();
        }

        @Override
        public void destroy() {
            if(processController!=null)
                processController.destroy();
            data.remove(getSessionId());
        }

        @Override
        public void send(String command) throws IOException {
            processController.write(command);
            lastTime = System.currentTimeMillis();
        }

        @Override
        public synchronized List<String> getOutputs() {
            try{
                return pool;
            }finally{
                lastTime = System.currentTimeMillis();
                pool = new LinkedList<String>();
            }

        }

        @Override
        public String getSessionId() {
            return sessionId;
        }

        @Override
        public void waitFor() {
            try {
                int ret = this.processController.waitFor();
                log.info("Process has exit with code:"+ret);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        @Override
        public synchronized void onOutput(String line) {
            pool.add(line);
        }

        @Override
        public synchronized void onExit(int code) {
            pool.add("Process has exit with code :" + code + "!");
        }

        @Override
        public synchronized void onException(Exception e) {
            pool.add("An Exception has happened!");
        }

        private long lastTime;

        private long maxAliveTime =  2 * 60 * 1000; //2分钟

        public boolean isTimout() {
            return System.currentTimeMillis() - lastTime > maxAliveTime;
        }
    }

    //为了保持进程活跃，单个session开启之后，必须在限定时间内调用 send和getOutputs 这两个方法，
    // 超过限定时间，则表示进程需要被杀死掉
    public static interface ShellSession extends Destroyable {

        /**
         * 销毁shell进程
         */
        void destroy();
        /**
         * 发送命令,如果失败，那么会直接退出shell进程
         * @param command
         */
        void send(String command) throws IOException;

        /**
         * 获取缓冲区的输出
         * @return
         */
        List<String> getOutputs();

        /**
         * 分配的id号码
         * @return
         */
        String getSessionId();


        void waitFor();
    }


    private Map<String,ShellSessionImpl> data = new ConcurrentHashMap<String, ShellSessionImpl>();

    public ShellSession openSession() throws IOException{
        ShellSessionImpl sheeSession= new ShellSessionImpl();
        data.put(sheeSession.getSessionId(),sheeSession);
        return sheeSession;
    }


    private static final Log log = LogFactory.getLog(ShellService.class);

    @Timer(every = Timer.Every.Minute)
    public synchronized void checkAlive(){
        //  log.info("正在检查shell alive");
        Iterator<String> it = data.keySet().iterator();
        while(it.hasNext()){
            String key = it.next();
            ShellSessionImpl session = data.get(key);
            if(session.isTimout()){
                log.info("正在关闭shell");
                session.destroy();
                it.remove();
            }
        }
    }

    public ShellSession getSession(String id){
        return data.get(id);
    }
}
